package lists;
/**
 * @author Michael Cole
 * Single Linked List
 */

public class SLList<E extends Comparable<E>>
{	
	private Node<E> head;
	private int listCount;
	
	/**
	 * LinkedList constructor
	 */
	public SLList()
	{
		// this is an empty list, so the reference to the head node
		// is set to a new node with no data
		head = new Node<E>(null);
		listCount = 0;
	}
	
	/**
	 * Adds object to the end of the list.
	 * @param data
	 */
	public void add(E data)
	{
		Node<E> temp = new Node<E>(data);
		Node<E> current = head;
		
		// starting at the head node, crawl to the end of the list
		while(current.getNext() != null)
		{
			current = current.getNext();
		}
		// the last node's "next" reference set to our new node
		current.setNext(temp);
		listCount++;// increment the number of elements variable
	}
	/**
	 * Adds object data to list at the int index
	 * if the index is out of bounds then it will just set it to be the last thing
	 * @param data
	 * @param index
	 */
	public void add(E data, int index)
	{
		Node<E> temp = new Node<E>(data);
		Node<E> current = head;
		// crawl to the requested index or the last element in the list,
		// whichever comes first
		for(int i = 1; i < index && current.getNext() != null; i++)
		{
			current = current.getNext();
		}
		// set the new node's next-node reference to this node's next-node reference
		temp.setNext(current.getNext());
		// now set this node's next-node reference to the new node
		current.setNext(temp);
		listCount++;// increment the number of elements variable
	}
	/**
	 * Returns the object at specified location
	 * @param index
	 * @return
	 */
	public Object get(int index)
	{
		// index must be 1 or higher
		if(index <= 0)
			return null;
		
		Node<E> current = head.getNext();
		for(int i = 1; i < index; i++)
		{
			if(current.getNext() == null)
				return null;
			
			current = current.getNext();
		}
		return current.getData();
	}
	/**
	 * removes the object at the location given. 
	 * @param index
	 * @return
	 */
	public boolean remove(int index)
	{
		// if the index is out of range, exit
		if(index < 1 || index > size())
			return false;
		
		Node<E> current = head;
		for(int i = 1; i < index; i++)
		{
			if(current.getNext() == null)
				return false;
			
			current = current.getNext();
		}
		current.setNext(current.getNext().getNext());
		listCount--; // decrement the number of elements variable
		return true;
	}
        
        
        /**
         * removes the first instance of the object of type E
         */
        public boolean remove(E x){
            Node<E> current = head;
            Node<E> previous = null;
            if(current.data.compareTo(x) == 0){
                head = head.getNext();
                return true;
            }
            while(current.data.compareTo(x) != 0 && current.getNext() != null){
                previous = current;
                current = current.getNext();
            }
            if(current.data.compareTo(x) == 0){
                previous.setNext(current.getNext());
                return true;
            }
            else{
                return false;
            }
        }
        
        /**
         * Removes every instance of the object of type E
         */
        public boolean removeAllE(E x){  
            boolean removed = false;
            while(head.data.compareTo(x) == 0){
                head = head.getNext();
                removed = true;
            }
            Node<E> firstInstance = head;
            Node<E> lastInstance = head;
            while(firstInstance.getNext() != null){
                
                while(lastInstance.data.compareTo(x) == 0 && lastInstance != null){
                    lastInstance.setNext(lastInstance.getNext());
                    removed = true;
                }
                firstInstance.setNext(lastInstance);
                firstInstance = lastInstance;
            }
            return removed;
            
        }
        
	
	/**
	 * Returns the size of the list.
	 * @return
	 */
	public int size()
	{
		return listCount;
	}
	/**
	 * returns a string of the list
	 */
	public String toString()
	{
		Node<E> current = (Node<E>) head.getNext();
		String output = "";
		while(current != null)
		{
			output += "[" + current.getData().toString() + "]";
			current = current.getNext();
		}
		return output;
	}
	
	/**
	 * 
	 * @author Michael Cole
	 * Node Class (for use with SLList)
	 *
	 */



	@SuppressWarnings("hiding")
	public class Node<E>
		{
			Node<E> next;
			E data;
			

			/**
			 * Create a node
			 * @param data
			 */
			public Node(E data)
			{
				this.next = null;
				this.data = data;
			}
			
			/**
			 * create a node with a specific pointer
			 * @param data
			 * @param next
			 */
			public Node(E data, Node<E> next)
			{
				this.next = next;
				this.data = data;
			}
			
			
			public Object getData()
			{
				return data;
			}
			
			public void setData(E data)
			{
				this.data = data;
			}
			
			public Node<E> getNext()
			{
				return next;
			}
			
			public void setNext(Node<E> next)
			{
				this.next = next;
			}
		}
	
	
}